package heap

import "unicode/utf16"

/*
字符串池
在java中可以将字符串放入字符串池中以节约空间，减少重复创建对象。如：
	String str1 = "Hello"; // 在字符串池中创建一个"Hello"对象
	String str2 = "Hello"; // 直接从字符串池中获取"Hello"对象
	System.out.println(str1 == str2); // 输出 true，因为str1和str2引用的是同一个对象



注：
	在class文件中字符串是以MUTF8格式保存的。
	在虚拟机运行期间字符串以String类对象形式存在。
	在String对象内部字符串以UTF16格式保存。
*/
// 创建一个map，其中key为go字符串，而value为java字符串(String类指针)
var internedStrings = map[string]*Object{}

// 根据Go字符串返回相应的java字符串， 因为我们字符串存储用的Go字符串
func JString(loader *ClassLoader, goString string) *Object {
	// 已经存在，直接返回即可
	if internedStr, ok := internedStrings[goString]; ok {
		return internedStr
	}
	// Go字符串转化为字符数组
	chars := stringToUtf16(goString)
	// 使用一个char数组类来存储 chars
	jChars := &Object{loader.LoadClass("[C"), chars, nil, 1}

	jStr := loader.LoadClass("java/lang/String").NewObject()
	// 直接将当前字段设置为String实例对象
	jStr.SetRefVar("value", "[C", jChars)

	internedStrings[goString] = jStr
	return jStr
}

func stringToUtf16(s string) []uint16 {
	runes := []rune(s)
	return utf16.Encode(runes)
}

// 把Java字符串转化为Go字符串
func GoString(javaString *Object) string {
	charArr := javaString.GetRefVar("value", "[C")
	return utf16ToString(charArr.Chars())
}

func utf16ToString(s []uint16) string {
	runes := utf16.Decode(s)
	return string(runes)
}

// 判断字符串是否进入字符串池，未进入就放入并返回该字符串
func InternString(jStr *Object) *Object {
	goStr := GoString(jStr)
	if internedStr, ok := internedStrings[goStr]; ok {
		return internedStr
	}
	internedStrings[goStr] = jStr
	return jStr
}
